-- -------------------------------------------------------------------------
-- Features/Self.lua (API Version)
-- Player self modifications and options
-- -------------------------------------------------------------------------

local module = {}

-- [Dependencies]
local ui = nil
local config = nil
local natives = nil
local lang = nil
local util_lib = nil
local debugger = nil
local features_config = nil

-- [Lua 5.4] <const> for function references
local type <const> = type
local os_clock <const> = os.clock
local math_floor <const> = math.floor
local pcall <const> = pcall

-- Module name for config storage
local MODULE_NAME <const> = "Self"

-- -------------------------------------------------------------------------
-- [Configuration Constants]
-- -------------------------------------------------------------------------

-- [Lua 5.4] <const> for player defaults
local PLAYER_DEFAULT_ALPHA <const> = 255
local PLAYER_DEFAULT_RUN_SPEED <const> = 1.0
local PLAYER_DEFAULT_SWIM_SPEED <const> = 1.0
local PLAYER_FAST_RUN_SPEED <const> = 1.49
local PLAYER_FAST_RUN_MIN <const> = 1.0
local PLAYER_FAST_RUN_MAX <const> = 1.49
local PLAYER_FAST_RUN_STEP <const> = 0.01

-- [Lua 5.4] <const> for cache durations (seconds)
local WANTED_LEVEL_CHECK_INTERVAL <const> = 0.5
local AUTO_CLEAN_INTERVAL <const> = 1.0

-- [Lua 5.4] <const> for health/armor values
local ARMOR_REPLENISH_AMOUNT <const> = 100

-- [Natives Cache]
local N = {}

-- [Local State]
local state = {
    subtab_idx = 1,

    -- Feature toggles
    god_mode = false,
    super_jump = false,
    invisibility = false,
    never_wanted = false,
    auto_clean = false,
    fast_run = false,
    fast_swim = false,
    no_ragdoll = false,

    -- Values
    player_alpha = PLAYER_DEFAULT_ALPHA,
    run_speed_mult = PLAYER_FAST_RUN_SPEED,

    -- Cache/timers
    last_alpha = -1,
    last_wanted_check = 0,
    last_auto_clean = 0,
    last_god_applied = false,
    last_invisibility = false,
    last_fast_run = false,
    last_fast_swim = false,
    last_run_speed_mult = PLAYER_DEFAULT_RUN_SPEED,
    last_swim_speed_mult = PLAYER_DEFAULT_SWIM_SPEED,
    last_no_ragdoll = nil,
    last_proofs_active = nil,

    last_proofs = {
        bullet = nil, fire = nil, explosion = nil,
        collision = nil, melee = nil, drown = nil
    },

    -- Damage proofs
    proofs = {
        bullet = false, fire = false, explosion = false,
        collision = false, melee = false, drown = false
    }
}

-- -------------------------------------------------------------------------
-- Internal Helper Functions
-- -------------------------------------------------------------------------

local function _T(text)
    if _G and _G._T then
        return _G._T(text)
    end
    if lang then
        if lang.get then return lang.get(text) end
        if lang.get_text then return lang.get_text(text) end
    end
    return text
end

local function show_toast(text, type_str)
    if ui and ui.notify then
        ui.notify(text, type_str or "Info")
    elseif util_lib and util_lib.toast then
        util_lib.toast(text)
    end
end

local function is_valid_handle(h)
    return type(h) == "number" and h ~= 0
end

local function safe_call(fn, ...)
    if not fn then return nil end
    local ok, result = pcall(fn, ...)
    if ok then return result end
    return nil
end

-- -------------------------------------------------------------------------
-- Config Save/Load Helpers
-- -------------------------------------------------------------------------

local function load_config()
    if not features_config then return end

    local saved = features_config.get_module(MODULE_NAME)
    if not saved then return end

    -- Load toggles
    if saved.god_mode ~= nil then state.god_mode = saved.god_mode end
    if saved.super_jump ~= nil then state.super_jump = saved.super_jump end
    if saved.invisibility ~= nil then state.invisibility = saved.invisibility end
    if saved.never_wanted ~= nil then state.never_wanted = saved.never_wanted end
    if saved.auto_clean ~= nil then state.auto_clean = saved.auto_clean end
    if saved.fast_run ~= nil then state.fast_run = saved.fast_run end
    if saved.fast_swim ~= nil then state.fast_swim = saved.fast_swim end
    if saved.no_ragdoll ~= nil then state.no_ragdoll = saved.no_ragdoll end

    -- Load values
    if saved.player_alpha ~= nil then state.player_alpha = saved.player_alpha end
    if saved.run_speed_mult ~= nil then state.run_speed_mult = saved.run_speed_mult end

    -- Load proofs
    if saved.proofs and type(saved.proofs) == "table" then
        for k, v in pairs(saved.proofs) do
            if state.proofs[k] ~= nil then
                state.proofs[k] = v
            end
        end
    end

    if debugger then debugger.debug("[Self] Config loaded") end
end

local function save_config()
    if not features_config then return end

    features_config.set_module(MODULE_NAME, {
        god_mode = state.god_mode,
        super_jump = state.super_jump,
        invisibility = state.invisibility,
        never_wanted = state.never_wanted,
        auto_clean = state.auto_clean,
        fast_run = state.fast_run,
        fast_swim = state.fast_swim,
        no_ragdoll = state.no_ragdoll,
        player_alpha = state.player_alpha,
        run_speed_mult = state.run_speed_mult,
        proofs = state.proofs
    }, true)
end

-- -------------------------------------------------------------------------
-- Module Interface
-- -------------------------------------------------------------------------

--- Initialize the Self module with required dependencies
-- @param ui_lib table The UI rendering library
-- @param cfg_lib table The configuration management library
-- @param nat_lib table The native functions library
-- @param lang_lib table The language/translation library
-- @param json_lib table The JSON encoding/decoding library (unused)
-- @param lexis_lib table The Lexis API library
-- @param debug_lib table The debugger library
-- @return boolean true on success
function module.init(ui_lib, cfg_lib, nat_lib, lang_lib, json_lib, _, debug_lib)
    ui = ui_lib
    config = cfg_lib
    natives = nat_lib
    lang = lang_lib
    debugger = debug_lib

    assert(ui, "[Self] UI library is required")
    assert(config, "[Self] Config library is required")

    -- Load features config module
    local ok, fc = pcall(require, "ClickUl.Config.features")
    if ok and fc then
        features_config = fc
    end

    if debugger then
        debugger.info("[Self] Module initialized successfully")
    end

    if _G.util then util_lib = _G.util end
    if json_lib and type(json_lib) == "table" and json_lib.yield then util_lib = json_lib end

    -- Cache Natives (Still needed for God Mode / Visuals)
    if natives then
        local function get(name) return natives[name] end

        N.PLAYER_ID = get("PLAYER_ID")
        N.PLAYER_PED_ID = get("PLAYER_PED_ID")
        N.SET_PLAYER_INVINCIBLE = get("SET_PLAYER_INVINCIBLE")
        N.SET_SUPER_JUMP_THIS_FRAME = get("SET_SUPER_JUMP_THIS_FRAME")
        N.SET_ENTITY_VISIBLE = get("SET_ENTITY_VISIBLE")
        N.SET_ENTITY_PROOFS = get("SET_ENTITY_PROOFS")
        N.CLEAR_PED_BLOOD_DAMAGE = get("CLEAR_PED_BLOOD_DAMAGE")
        N.RESET_PED_VISIBLE_DAMAGE = get("RESET_PED_VISIBLE_DAMAGE")
        N.GET_PLAYER_WANTED_LEVEL = get("GET_PLAYER_WANTED_LEVEL")
        N.CLEAR_PLAYER_WANTED_LEVEL = get("CLEAR_PLAYER_WANTED_LEVEL")
        N.SET_PLAYER_WANTED_LEVEL = get("SET_PLAYER_WANTED_LEVEL")
        N.SET_PLAYER_WANTED_LEVEL_NOW = get("SET_PLAYER_WANTED_LEVEL_NOW")
        N.SET_ENTITY_ALPHA = get("SET_ENTITY_ALPHA")
        N.RESET_ENTITY_ALPHA = get("RESET_ENTITY_ALPHA")
        N.SET_PED_SWEAT = get("SET_PED_SWEAT")
        N.SET_ENTITY_HEALTH = get("SET_ENTITY_HEALTH")
        N.GET_ENTITY_MAX_HEALTH = get("GET_ENTITY_MAX_HEALTH")
        N.ADD_ARMOUR_TO_PED = get("ADD_ARMOUR_TO_PED")
        N.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER = get("SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER")
        N.SET_SWIM_MULTIPLIER_FOR_PLAYER = get("SET_SWIM_MULTIPLIER_FOR_PLAYER")
        N.SET_PED_CAN_RAGDOLL = get("SET_PED_CAN_RAGDOLL")
        N.APPLY_DAMAGE_TO_PED = get("APPLY_DAMAGE_TO_PED")

        if debugger then debugger.debug("[Self] Natives cached successfully") end
    end

    -- Load saved config
    load_config()

    return true
end

--- Background update loop for Self module
-- Applies all enabled self modifications every frame
function module.on_tick()
    if not natives or not N.PLAYER_ID or not N.PLAYER_PED_ID then return end

    local player_id = N.PLAYER_ID()
    local ped = N.PLAYER_PED_ID()

    -- Validate ped (PlayerId can legitimately be 0 for local player)
    if not player_id or not ped or ped == 0 then
        return
    end

    -- 1. God mode (with state change detection)
    if state.god_mode ~= state.last_god_applied then
        if N.SET_PLAYER_INVINCIBLE then
            pcall(N.SET_PLAYER_INVINCIBLE, player_id, state.god_mode)
        end
        state.last_god_applied = state.god_mode
    end

    -- 2. Super jump
    if state.super_jump and N.SET_SUPER_JUMP_THIS_FRAME then
        pcall(N.SET_SUPER_JUMP_THIS_FRAME, player_id)
    end

    -- 3. Speed Multipliers (with caching to avoid redundant calls)
    if state.fast_run then
        if not state.last_fast_run or state.run_speed_mult ~= state.last_run_speed_mult then
            if N.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER then
                pcall(N.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER, player_id, state.run_speed_mult)
            end
            state.last_fast_run = true
            state.last_run_speed_mult = state.run_speed_mult
        end
    elseif state.last_fast_run then
        if N.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER then
            pcall(N.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER, player_id, PLAYER_DEFAULT_RUN_SPEED)
        end
        state.last_fast_run = false
        state.last_run_speed_mult = PLAYER_DEFAULT_RUN_SPEED
    end

    if state.fast_swim then
        if not state.last_fast_swim or state.run_speed_mult ~= state.last_swim_speed_mult then
            if N.SET_SWIM_MULTIPLIER_FOR_PLAYER then
                pcall(N.SET_SWIM_MULTIPLIER_FOR_PLAYER, player_id, state.run_speed_mult)
            end
            state.last_fast_swim = true
            state.last_swim_speed_mult = state.run_speed_mult
        end
    elseif state.last_fast_swim then
        if N.SET_SWIM_MULTIPLIER_FOR_PLAYER then
            pcall(N.SET_SWIM_MULTIPLIER_FOR_PLAYER, player_id, PLAYER_DEFAULT_SWIM_SPEED)
        end
        state.last_fast_swim = false
        state.last_swim_speed_mult = PLAYER_DEFAULT_SWIM_SPEED
    end

    -- 4. No Ragdoll (with caching)
    if state.no_ragdoll then
        if state.last_no_ragdoll ~= true and N.SET_PED_CAN_RAGDOLL then
            pcall(N.SET_PED_CAN_RAGDOLL, ped, false)
            state.last_no_ragdoll = true
        end
    else
        if state.last_no_ragdoll == true and N.SET_PED_CAN_RAGDOLL then
            pcall(N.SET_PED_CAN_RAGDOLL, ped, true)
            state.last_no_ragdoll = false
        end
    end

    -- 5. Invisibility (with caching to avoid redundant calls)
    if state.invisibility then
        if not state.last_invisibility and N.SET_ENTITY_VISIBLE then
            pcall(N.SET_ENTITY_VISIBLE, ped, false, 0)
            state.last_invisibility = true
        end
    elseif state.last_invisibility then
        if N.SET_ENTITY_VISIBLE then
            pcall(N.SET_ENTITY_VISIBLE, ped, true, 0)
        end
        -- Reset alpha to fully opaque when disabling invisibility
        if N.RESET_ENTITY_ALPHA then
            pcall(N.RESET_ENTITY_ALPHA, ped)
        elseif N.SET_ENTITY_ALPHA then
            pcall(N.SET_ENTITY_ALPHA, ped, PLAYER_DEFAULT_ALPHA, false)
        end
        state.last_alpha = PLAYER_DEFAULT_ALPHA
        state.last_invisibility = false
    end

    -- 6. Damage immunity (with change detection)
    if N.SET_ENTITY_PROOFS then
        local p = state.proofs
        local lp = state.last_proofs
        local proofs_changed = p.bullet ~= lp.bullet or p.fire ~= lp.fire or
                               p.explosion ~= lp.explosion or p.collision ~= lp.collision or
                               p.melee ~= lp.melee or p.drown ~= lp.drown

        if proofs_changed then
            local any = p.bullet or p.fire or p.explosion or p.collision or p.melee or p.drown
            if any then
                pcall(N.SET_ENTITY_PROOFS, ped, p.bullet, p.fire, p.explosion, p.collision, p.melee, false, true, p.drown)
            else
                pcall(N.SET_ENTITY_PROOFS, ped, false, false, false, false, false, false, false, false)
            end
            -- Update cache
            lp.bullet, lp.fire, lp.explosion = p.bullet, p.fire, p.explosion
            lp.collision, lp.melee, lp.drown = p.collision, p.melee, p.drown
        end
    end

    -- 7. Auto clean (throttled)
    local now = os_clock()
    if state.auto_clean and now > state.last_auto_clean then
        if N.CLEAR_PED_BLOOD_DAMAGE then
            pcall(N.CLEAR_PED_BLOOD_DAMAGE, ped)
        end
        if N.RESET_PED_VISIBLE_DAMAGE then
            pcall(N.RESET_PED_VISIBLE_DAMAGE, ped)
        end
        state.last_auto_clean = now + AUTO_CLEAN_INTERVAL
    end

    -- 8. Never wanted
    if state.never_wanted and now > state.last_wanted_check then
        if N.GET_PLAYER_WANTED_LEVEL then
            local ok, wanted_level = pcall(N.GET_PLAYER_WANTED_LEVEL, player_id)
            if ok and wanted_level and wanted_level > 0 then
                if N.CLEAR_PLAYER_WANTED_LEVEL then
                    pcall(N.CLEAR_PLAYER_WANTED_LEVEL, player_id)
                end
                if N.SET_PLAYER_WANTED_LEVEL_NOW then
                    pcall(N.SET_PLAYER_WANTED_LEVEL_NOW, player_id, false)
                end
            end
        end
        state.last_wanted_check = now + WANTED_LEVEL_CHECK_INTERVAL
    end

    -- 9. Transparency
    if math_floor(state.player_alpha) ~= state.last_alpha then
        local new_alpha = math_floor(state.player_alpha)
        if N.SET_ENTITY_ALPHA then
            pcall(N.SET_ENTITY_ALPHA, ped, new_alpha, false)
        end
        state.last_alpha = new_alpha
    end
end

--- Draw the Self module UI
-- Renders tabs for General and Visual options
function module.draw()
    state.subtab_idx = ui.SubTabBar({
        _T("General"),
        _T("Visual")
    }, state.subtab_idx or 1)

    local player_id = safe_call(N.PLAYER_ID) or 0
    local ped = safe_call(N.PLAYER_PED_ID) or 0

    -- TAB 1: General Options
    if state.subtab_idx == 1 then
        ui.DualColumn(
            function()
                ui.Group(_T("Protection"), function()
                    state.god_mode = ui.Checkbox(_T("God Mode"), state.god_mode)
                    state.never_wanted = ui.Checkbox(_T("Never Wanted"), state.never_wanted)
                    state.no_ragdoll = ui.Checkbox(_T("No Ragdoll"), state.no_ragdoll)

                    if ui.Button(_T("Replenish Health & Armor")) then
                        if not is_valid_handle(ped) then
                            show_toast(_T("Not in game"), "Warning")
                        else
                            if N.GET_ENTITY_MAX_HEALTH and N.SET_ENTITY_HEALTH then
                                local ok, max_hp = pcall(N.GET_ENTITY_MAX_HEALTH, ped)
                                if ok and max_hp then
                                    pcall(N.SET_ENTITY_HEALTH, ped, max_hp)
                                end
                            end
                            if N.ADD_ARMOUR_TO_PED then
                                pcall(N.ADD_ARMOUR_TO_PED, ped, ARMOR_REPLENISH_AMOUNT)
                            end
                            show_toast(_T("Health & Armor Restored"), "Success")
                            if debugger then debugger.debug("Health & Armor replenished") end
                        end
                    end
                end)

                ui.Group(_T("Movement"), function()
                    state.super_jump = ui.Checkbox(_T("Super Jump"), state.super_jump)

                    state.fast_run = ui.Checkbox(_T("Fast Run"), state.fast_run)
                    if state.fast_run then
                        state.run_speed_mult = ui.Slider(_T("Run Speed"), state.run_speed_mult, PLAYER_FAST_RUN_MIN, PLAYER_FAST_RUN_MAX, PLAYER_FAST_RUN_STEP)
                    end

                    state.fast_swim = ui.Checkbox(_T("Fast Swim"), state.fast_swim)
                end)
            end,
            function()
                ui.Group(_T("Immunities"), function()
                    state.proofs.bullet = ui.Checkbox(_T("Bullet Proof"), state.proofs.bullet)
                    state.proofs.fire = ui.Checkbox(_T("Fire Proof"), state.proofs.fire)
                    state.proofs.explosion = ui.Checkbox(_T("Explosion Proof"), state.proofs.explosion)
                    state.proofs.collision = ui.Checkbox(_T("Collision Proof"), state.proofs.collision)
                    state.proofs.melee = ui.Checkbox(_T("Melee Proof"), state.proofs.melee)
                    state.proofs.drown = ui.Checkbox(_T("Drown Proof"), state.proofs.drown)
                end)

                ui.Group(_T("Wanted Level"), function()
                    local current = 0
                    if is_valid_handle(ped) and N.GET_PLAYER_WANTED_LEVEL then
                        local ok, level = pcall(N.GET_PLAYER_WANTED_LEVEL, player_id)
                        if ok and level then
                            current = level
                        end
                    end
                    local new_w = ui.Slider(_T("Wanted Level"), current, 0, 5, 1)
                    if is_valid_handle(ped) and new_w ~= current and N.SET_PLAYER_WANTED_LEVEL then
                        pcall(N.SET_PLAYER_WANTED_LEVEL, player_id, new_w, false)
                        if N.SET_PLAYER_WANTED_LEVEL_NOW then
                            pcall(N.SET_PLAYER_WANTED_LEVEL_NOW, player_id, false)
                        end
                    end
                    if ui.Button(_T("Clear Wanted")) then
                        if not is_valid_handle(ped) then
                            show_toast(_T("Not in game"), "Warning")
                        else
                            if N.CLEAR_PLAYER_WANTED_LEVEL then
                                pcall(N.CLEAR_PLAYER_WANTED_LEVEL, player_id)
                            end
                        end
                    end
                end)
            end
        )

    -- TAB 2: Visuals
    elseif state.subtab_idx == 2 then
         ui.DualColumn(
            function()
                ui.Group(_T("Appearance"), function()
                    state.invisibility = ui.Checkbox(_T("Invisibility"), state.invisibility)
                    state.player_alpha = ui.Slider(_T("Transparency"), state.player_alpha, 0, PLAYER_DEFAULT_ALPHA, 1)

                     if ui.Button(_T("Clean Clothes")) then
                        if not is_valid_handle(ped) then
                            show_toast(_T("Not in game"), "Warning")
                        else
                            if N.CLEAR_PED_BLOOD_DAMAGE then
                                pcall(N.CLEAR_PED_BLOOD_DAMAGE, ped)
                            end
                            if N.RESET_PED_VISIBLE_DAMAGE then
                                pcall(N.RESET_PED_VISIBLE_DAMAGE, ped)
                            end
                            if N.SET_PED_SWEAT then
                                pcall(N.SET_PED_SWEAT, ped, 0.0)
                            end
                            show_toast(_T("Clothes Cleaned"), "Success")
                        end
                    end
                    state.auto_clean = ui.Checkbox(_T("Auto Clean"), state.auto_clean)
                end)
            end,
            function() end
        )
    end
end

function module.on_menu_close()
    save_config()
end

return module
